// <copyright file="ForEachLoopInstruction.cs" company="Codeplex project Eco.Contrib"> 
// The MIT License (MIT) 
// </copyright> 
// <author>Peter Buchmann</author> 

namespace Eco.Contrib
{
  using System;
  using System.Collections.Generic;
  using Eco.ObjectRepresentation;
  using Eco.UmlCodeAttributes;

    /// <summary>
    /// ForEachLoopInstruction: Instruction that represents a for each loop
    /// </summary>
  public partial class ForEachLoopInstruction 
  {
      /// <summary>
      /// Executes the function.
      /// </summary>
      /// <param name="serviceProvider">The service provider.</param>
      /// <param name="variableList">The variable list.</param>
      /// <returns>Return value of executed function</returns>
      [UmlElement(Id = "c52eb2fe-96c1-4b78-9bd0-bd3fd5f9c44c")]
      protected internal override IElement ExecuteFunction(IEcoServiceProvider serviceProvider, IModifiableVariableList variableList)
      {
          IElementCollection elementCollection = this.OclAndVariable.ExecuteFunction(serviceProvider, variableList).GetAsCollection();
          foreach (IElement loopElement in elementCollection)
          {
              this.OclAndVariable.ResultVariable.Set(variableList, loopElement);
              IElement element = LoopedInstruction.Execute(serviceProvider, variableList);
              if (element != null)
              {
                  return element;
              }
          }

          return null;
      }

      /// <summary>
      /// Inits the specified tokens.
      /// </summary>
      /// <param name="tokens">The tokens.</param>
      [UmlElement(Id = "e30036e1-72c9-40e8-bb2b-ee85faa27bdb")]
      protected internal override void Init(Dictionary<IList<EslToken>, Type> tokens)
      {
          List<EslToken> tokenList = new List<EslToken>();
          tokenList.Add(new EslToken(EslTokenType.Word, "For"));
          tokenList.Add(new EslToken(EslTokenType.Word, "Each"));
          tokenList.Add(new EslToken(EslTokenType.Word, "%"));
          tokenList.Add(new EslToken(EslTokenType.Operator, ":="));
          tokenList.Add(new EslToken(EslTokenType.Word, "Ocl"));
          tokenList.Add(new EslToken(EslTokenType.Operator, "("));
          tokens.Add(tokenList, this.GetType());
          tokenList = new List<EslToken>();
          tokenList.Add(new EslToken(EslTokenType.Word, "For"));
          tokenList.Add(new EslToken(EslTokenType.Word, "Each"));
          tokenList.Add(new EslToken(EslTokenType.Word, "%"));
          tokenList.Add(new EslToken(EslTokenType.Operator, ":="));
          tokenList.Add(new EslToken(EslTokenType.Word, "Eal"));
          tokenList.Add(new EslToken(EslTokenType.Operator, "("));
          tokens.Add(tokenList, this.GetType());
          tokenList = new List<EslToken>();
          tokenList.Add(new EslToken(EslTokenType.Word, "For"));
          tokenList.Add(new EslToken(EslTokenType.Word, "Each"));
          tokenList.Add(new EslToken(EslTokenType.Word, "%"));
          tokenList.Add(new EslToken(EslTokenType.Operator, ":="));
          tokenList.Add(new EslToken(EslTokenType.Word, "OclPs"));
          tokenList.Add(new EslToken(EslTokenType.Operator, "("));
          tokens.Add(tokenList, this.GetType());
      }

      /// <summary>
      /// Parses the specified token list.
      /// </summary>
      /// <param name="tokenList">The token list.</param>
      /// <param name="ecoScript">The eco script.</param>
      /// <param name="fullExpression">if set to <c>true</c> [full expression].</param>
      [UmlElement(Id = "0498362d-4ffc-4e35-9eee-712a9d20c480")]
      protected internal override void Parse(IList<EslToken> tokenList, EcoScript ecoScript, bool fullExpression)
      {
          EslToken token = GetNextToken(tokenList); // For
          RememberEcoScript(ecoScript, token);
          token = GetNextToken(tokenList); // Each

          EslInstruction instruction = ecoScript.ReadNextInstruction(tokenList, false);
          if (instruction is InstructionWithOclAndVariable)
          {
              this.OclAndVariable = instruction as InstructionWithOclAndVariable;
          }
          else
          {
              throw new EslException("variable := EAL|OCL|OCLPS(..) expected after " + token.ToString(), token);
          }

          token = GetNextToken(tokenList);
          if ((token.TokenType != EslTokenType.Word) || (token.TokenValue.ToLower() != "do"))
          {
              throw new EslException("keyword 'do' expected at token " + token.ToString(), token);
          }

          // Read loop block
          bool finished = false;
          EslInstruction lastInstruction = null;
          instruction = null;
          do
          {
              instruction = ecoScript.ReadNextInstruction(tokenList, true);
              if (instruction != null)
              {
                  if (lastInstruction == null)
                  {
                      this.LoopedInstruction = instruction;
                  }
                  else
                  {
                      lastInstruction.NextInstruction = instruction;
                  }

                  lastInstruction = instruction;
              }

              finished = (instruction == null) || (tokenList.Count == 0) ||
                  ((tokenList[0].TokenType == EslTokenType.Word) && (tokenList[0].TokenValue.ToLower() == "end"));
          }
          while (!finished);

          token = GetNextToken(tokenList);
          if ((token.TokenType != EslTokenType.Word) || (token.TokenValue.ToLower() != "end"))
          {
              throw new EslException("keyword 'end' expected at token " + token.ToString(), token);
          }

          if (fullExpression)
          {
              EslToken tokenEnd = GetNextToken(tokenList);
              AssertInstructionEnd(tokenEnd);
          }
      }
  }
}